using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using HunterPluginSDK;
using HunterPluginSDK.Interface;


namespace Engine.IO
{
    public enum Target{Source, Destination};
    public enum FileType { All, Pics, Vids };
    public class IOException:Exception
    {
        public Target Target;
        public string Path;
        public IOException(Target t, string p):base(p)
        {
            Path = p;
            Target = t;
        }
    }
    /// <summary>
    /// Class implements general IO operations
    /// </summary>
    public class FileTools
    {
        #region MEMBERS
        private Business.Interfaces.IMatchFile FileComparer;
        Engine.CSV.ProcessType TypeOfProcess;
        IO.CRC32 CRCCalculator = null;
        private  IProgressHandler NotifyProgress;

        static private System.Collections.Generic.Dictionary<string, long> CRC32cache
            = new System.Collections.Generic.Dictionary<string, long>();//static cache for files CRC32

        #endregion

        #region CTOR

        public FileTools(Business.Interfaces.IMatchFile fc, 
            Engine.CSV.ProcessType pt,
            IProgressHandler ph)
        {
            FileComparer = fc;
            CRCCalculator = new IO.CRC32();
            TypeOfProcess = pt;
            NotifyProgress = ph;
        }
        #endregion

        #region PUBLIC

        public long GetFileCRC32(System.IO.FileInfo fi)
        {
            NotifyProgress.WriteToOutput("GetFileCRC32 begins for " + fi.FullName, LogPriority.DEBUG);

            long crc = 0;

            if(DataSchema.Settings.SettingsMgr.UseCRC32Chache && CRC32cache.ContainsKey(fi.FullName))
            {
                NotifyProgress.WriteToOutput("Got CRC32 from cache for " + fi.FullName, LogPriority.DEBUG);
                crc = CRC32cache[fi.FullName];
            }
            else
            {
                FileStream fs = fi.OpenRead();
                crc = this.CRCCalculator.CRC(fs);
                fs.Close();
                if(DataSchema.Settings.SettingsMgr.UseCRC32Chache)
                    CRC32cache.Add(fi.FullName, crc);
            }
            NotifyProgress.WriteToOutput("GetFileCRC32 ends for " + fi.FullName, LogPriority.DEBUG);
            return crc;
        }

        public bool VerifyFile(System.IO.FileInfo fi, Engine.Business.Abstract.CollectionItem c)
        {
            if (fi.Exists)
            {
                //first compare size
                if (c.Size == fi.Length)
                {
                    if (TypeOfProcess == CSV.ProcessType.Hunt)
                    {
                        long crc = 0;

                        if(DataSchema.Settings.SettingsMgr.UseCRC32Chache && CRC32cache.ContainsKey(fi.FullName))
                            crc = CRC32cache[fi.FullName];
                        else
                        {
                            FileStream fs = fi.OpenRead();
                            crc = this.CRCCalculator.CRC(fs);
                            fs.Close();
                            if(DataSchema.Settings.SettingsMgr.UseCRC32Chache)
                                CRC32cache.Add(fi.FullName, crc);
                        }
                        
                        if (c.CRC32 == crc)
                            return true;
                    }
                    else if (TypeOfProcess == CSV.ProcessType.Verify)
                        return true;
                }
            }
            return false;
        }

        public int Search(string[] paths, ref bool cont)
        {
            int i = 0;
            foreach(string p in paths)
            {
                FileSystemEnumerator fe = new FileSystemEnumerator(p, "*.*", true);

                foreach(FileInfo fi in fe.Matches())
                {
                    if (cont == false)
                        break;

                    Business.Abstract.CollectionItem c = FileComparer.MatchFile(fi);
                    if(c != null)//item found
                    {
                        if(FileComparer.ProcessFile(fi,c))
                            ++i;
                    }
                }
            }
            return i;
        }
    
        public int GetFilesCount(string[] paths)
        {
            int i = 0;
            foreach(string p in paths)
            {
                FileSystemEnumerator fe = new FileSystemEnumerator(p, "*.*", true);

                foreach(FileInfo fi in fe.Matches())
                {
                    ++i;
                }
            }
            return i;
        }
        #endregion

        #region STATIC

        static private void ValidatePath(Target t, string path)
        {
            System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(path);
            if(dir.Exists)
                return;
            System.IO.FileInfo file = new System.IO.FileInfo(path);
            if(file.Exists)
                return;

            throw new IOException(t, "File or Directory " + path +" not exists");
        }

    	public static string GetSize(double size)
	    {
		    double s = size;

		    string[] format = new string[] {"{0} bytes", "{0} KB","{0} MB", "{0} GB", "{0} TB", "{0} PB", "{0} EB"};

		    int i = 0;

		    while (i < format.Length && s >= 1024) {

			    s = (double) (100 * s / 1024) / 100.0;
			    i++;
		    }
            s = Math.Round(s, 2);
		    return string.Format(format[i],s);
	    }


        static public void DeleteFile(string path, bool delfold)
        { 
            FileInfo f = new FileInfo(path);
            
            if(f.Exists)
            {
                f.Delete();
                if(delfold)
                    RemoveEmptyFolder(f.Directory);
            }
        }

        static void RemoveEmptyFolder(DirectoryInfo di)
        {
            if((di.GetFiles()).Length == 0 && di.GetDirectories().Length == 0)
            {
                di.Delete(true);
                RemoveEmptyFolder(di.Parent);
            }
        }

        static public bool FileExist(string path)
        {
            FileInfo f = new FileInfo(path);
            return (f.Exists);
        }

        static public bool OpenFile(string imgpath, string arg, bool skip)
        {
            if(skip == false && FileExist(imgpath) == false)
            {
                return false;
            }
            ProcessStartInfo pInfo = new ProcessStartInfo();
            pInfo.FileName = imgpath;
            pInfo.Arguments = arg;
            pInfo.UseShellExecute = true;
            Process p = Process.Start(pInfo);
            return true;
        }

        static public void CopyFolder(string sourcep, string dest, FileType tp)
        {
            ValidatePath(Target.Source, sourcep);
            ValidatePath(Target.Destination, dest);

            System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(sourcep);
            System.IO.DirectoryInfo dirdest = new System.IO.DirectoryInfo(dest);

            dirdest = dirdest.CreateSubdirectory(dir.Name);

            foreach(System.IO.FileInfo fi in dir.GetFiles())
            {
                FileInfo finew = new FileInfo(dirdest.FullName + "\\" + fi.Name);
                if (finew.Exists == false)
                {
                    if (tp == FileType.Pics && DataSchema.CollectionSchema.IsItemPic(fi.FullName) == false)
                        break;
                    if (tp == FileType.Vids && DataSchema.CollectionSchema.IsItemVid(fi.FullName) == false)
                        break;

                    fi.CopyTo(dirdest.FullName + "\\" + fi.Name, false);
                }
            }
            foreach(System.IO.DirectoryInfo di in dir.GetDirectories())
            {
                CopyFolder(di.FullName, dirdest.FullName, tp);
            }            

        }

        static public void CopyItem(System.IO.FileInfo fi, string dest, IProgressHandler ph)
        {
            DateTime t = DateTime.Now;
            System.IO.FileInfo finew = new FileInfo(dest);
            
            if(finew.Directory.Exists == false)
                finew.Directory.Create();

            fi.CopyTo(dest);
            TimeSpan sp = DateTime.Now - t;
            ph.WriteToOutput("Copy operations lasts " + sp.ToString() + " for file " + fi.Name, LogPriority.DEBUG);
            ph.WriteToOutput(fi.Name + " was hunted.", LogPriority.INFO);
        }

        #endregion
    }
}
